﻿// ****************************************************************************** //
//     Module for all functions and variables used to move the cars properly.     //
//     The module handles also the logic to check if the frog intersects          //
//              or crashes onto a car and, thus, loses one life                   //
// ****************************************************************************** //

module CarsSimulation

open Microsoft.Xna.Framework
open Microsoft.Xna.Framework.Input
open Microsoft.Xna.Framework.Graphics
open Utilities
open Casanova
open Coroutines
open Math
open SI
open CarsTypes
open FrogSimulation
open MapGrid
open GameState

let mutable collision_detected: bool = false //true if there is a collision between car and frog


// ****************************************************************************** //
//          Main update function to handle car movements and collisions           //
// ****************************************************************************** //
let rec update_state(dt:float32) = 

  if starting_position = true then 
    collision_detected <- false
    FrogSimulation.win_trigger <- 0 //bug fixing: need this here to stop eventual crash sound
                                    // if winbox is missed and the game needs to game over!
                                    //Without this here, the crash sound would play forever
                                    // in loop until the next game starts...

  //for cycle to check if there is a collision between cars & frog
  //it removes also cars when they go outside the screen boundaries
  for c in !cars_state.Cars do
    if starting_position = false
       then
        if c.box.Intersects(FrogTypes.frog_state.sphere) && collision_detected = false
            then 
                collision_detected <- true
                game_state.Life <- game_state.Life - 1
                game_state.time_counter <- 0.0f
                FrogSimulation.reset_state() |> ignore
                starting_position <- true

    update_cars c dt
    cars_state.Cars := [
        for a in !cars_state.Cars do
            if !a.PositionX > SPAWNING_X then
                yield a]

  for c in !cars_state.Cars2 do
    if starting_position = false
       then
        if c.box.Intersects(FrogTypes.frog_state.sphere) && collision_detected = false
            then 
                collision_detected <- true
                game_state.Life <- game_state.Life - 1
                game_state.time_counter <- 0.0f
                FrogSimulation.reset_state() |> ignore
                starting_position <- true

    update_cars c dt
    cars_state.Cars2 := [
        for a in !cars_state.Cars2 do
            if !a.PositionX > SPAWNING_X then
                yield a]

  for c in !cars_state.Cars3 do
    if starting_position = false
       then
        if c.box.Intersects(FrogTypes.frog_state.sphere) && collision_detected = false
            then 
                collision_detected <- true
                game_state.Life <- game_state.Life - 1
                game_state.time_counter <- 0.0f
                FrogSimulation.reset_state() |> ignore
                starting_position <- true

    update_cars c dt
    cars_state.Cars3 := [
        for a in !cars_state.Cars3 do
            if !a.PositionX < -SPAWNING_X then
                yield a]

  for c in !cars_state.Cars4 do
    if starting_position = false
       then
        if c.box.Intersects(FrogTypes.frog_state.sphere) && collision_detected = false
            then 
                collision_detected <- true
                game_state.Life <- game_state.Life - 1
                game_state.time_counter <- 0.0f
                FrogSimulation.reset_state() |> ignore
                starting_position <- true

    update_cars c dt
    cars_state.Cars4 := [
        for a in !cars_state.Cars4 do
            if !a.PositionX < -SPAWNING_X then
                yield a]


//update function to give cars some speed
and private update_cars (c:Car) (dt:float32) =
  c.PositionX := !c.PositionX + dt * c.VelX
  c.box.Min.X <- c.box.Min.X + dt * c.VelX //bounding box car update
  c.box.Max.X <- c.box.Max.X + dt * c.VelX //bounding box car update

let private (!) = immediate_read
let private (:=) = immediate_write

//function (coroutine) to create new cars
let spawn_cars() = 
    co{
      do! yield_

      if cars_state.Cars.Value.IsEmpty //if no car has been created yet
        then //create a row of cars
        cars_state.Cars :=
        { 
          PositionX = variable(-SPAWNING_X)
          PositionY = variable(ROW1_Y)
          PositionZ = variable(ROW1_Z)
          box = new BoundingBox(new Vector3(-SPAWNING_X - 4.2f,ROW1_Y + (1.3f), ROW1_Z + (2.0f)),new Vector3(-SPAWNING_X + 4.2f,ROW1_Y-(0.8f),ROW1_Z + (-1.0f)))
          VelX = SPEED_ROW1
        } :: !cars_state.Cars

      if not cars_state.Cars.Value.IsEmpty
        then
        let mutable a : Car = cars_state.Cars.Value.Head //get the first car of the row

        if a.PositionX.Value < SPAWN_LIMIT_ROW1 //if the first car is almost going outside the screen
            then //time to create a new car
            cars_state.Cars :=
            { 
              PositionX = variable(-SPAWNING_X)
              PositionY = variable(ROW1_Y)
              PositionZ = variable(ROW1_Z)
              box = new BoundingBox(new Vector3(-SPAWNING_X - 4.2f,ROW1_Y + (1.3f), ROW1_Z + (2.0f)),new Vector3(-SPAWNING_X + 4.2f,ROW1_Y-(0.8f),ROW1_Z + (-1.0f)))
              VelX = SPEED_ROW1
            } :: !cars_state.Cars
            

      if cars_state.Cars2.Value.IsEmpty
        then   
        cars_state.Cars2 :=
        { 
          PositionX = variable(-SPAWNING_X)
          PositionY = variable(ROW2_Y)
          PositionZ = variable(ROW2_Z)
          box = new BoundingBox(new Vector3(-SPAWNING_X - 4.2f,ROW2_Y + (1.3f), ROW2_Z + (2.0f)),new Vector3(-SPAWNING_X + 4.2f,ROW2_Y-(0.8f),ROW2_Z + (-1.0f)))
          VelX = SPEED_ROW2
        } :: !cars_state.Cars2

      if not cars_state.Cars2.Value.IsEmpty
        then
        let mutable a : Car = cars_state.Cars2.Value.Head
 
        if a.PositionX.Value < SPAWN_LIMIT_ROW2
            then
            cars_state.Cars2 :=
            { 
                PositionX = variable(-SPAWNING_X)
                PositionY = variable(ROW2_Y)
                PositionZ = variable(ROW2_Z)
                box = new BoundingBox(new Vector3(-SPAWNING_X - 4.2f,ROW2_Y + (1.3f), ROW2_Z + (2.0f)),new Vector3(-SPAWNING_X + 4.2f,ROW2_Y-(0.8f),ROW2_Z + (-1.0f)))
                VelX = SPEED_ROW2
            } :: !cars_state.Cars2

      if cars_state.Cars3.Value.IsEmpty
        then   
        cars_state.Cars3 :=
        { 
          PositionX = variable(SPAWNING_X)
          PositionY = variable(ROW3_Y)
          PositionZ = variable(ROW3_Z)
          box = new BoundingBox(new Vector3(SPAWNING_X - 4.2f,ROW3_Y + (1.3f), ROW3_Z + (2.0f)),new Vector3(SPAWNING_X + 4.2f,ROW3_Y-(0.8f),ROW3_Z + (-1.0f)))
          VelX = SPEED_ROW3
        } :: !cars_state.Cars3

      if not cars_state.Cars3.Value.IsEmpty
        then
        let mutable a : Car = cars_state.Cars3.Value.Head
 
        if a.PositionX.Value > SPAWN_LIMIT_ROW3
            then
            cars_state.Cars3 :=
            { 
                PositionX = variable(SPAWNING_X)
                PositionY = variable(ROW3_Y)
                PositionZ = variable(ROW3_Z)
                box = new BoundingBox(new Vector3(SPAWNING_X - 4.2f,ROW3_Y + (1.3f), ROW3_Z + (2.0f)),new Vector3(SPAWNING_X + 4.2f,ROW3_Y-(0.8f),ROW3_Z + (-1.0f)))
                VelX = SPEED_ROW3
            } :: !cars_state.Cars3


      if cars_state.Cars4.Value.IsEmpty
        then   
        cars_state.Cars4 :=
        { 
          PositionX = variable(SPAWNING_X)
          PositionY = variable(ROW4_Y)
          PositionZ = variable(ROW4_Z)
          box = new BoundingBox(new Vector3(SPAWNING_X - 4.2f,ROW4_Y + (1.3f), ROW4_Z + (2.0f)),new Vector3(SPAWNING_X + 4.2f,ROW4_Y-(0.8f),ROW4_Z + (-1.0f)))
          VelX = SPEED_ROW4
        } :: !cars_state.Cars4

      if not cars_state.Cars4.Value.IsEmpty
        then
        let mutable a : Car = cars_state.Cars4.Value.Head
 
        if a.PositionX.Value > SPAWN_LIMIT_ROW4
            then
            cars_state.Cars4 :=
            { 
                PositionX = variable(SPAWNING_X)
                PositionY = variable(ROW4_Y)
                PositionZ = variable(ROW4_Z)
                box = new BoundingBox(new Vector3(SPAWNING_X - 4.2f,ROW4_Y + (1.3f), ROW4_Z + (2.0f)),new Vector3(SPAWNING_X + 4.2f,ROW4_Y-(0.8f),ROW4_Z + (-1.0f)))
                VelX = SPEED_ROW4
            } :: !cars_state.Cars4
      return()
      }

